
#include "Shader.h"

Shader::Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath) {
	std::string vertexCode = LoadShader(vertexPath);
	std::string fragmentCode = LoadShader(fragmentPath);
	std::string geometryCode = "";
	if (geometryPath != nullptr)
		geometryCode = LoadShader(geometryPath);

	if (vertexCode == "" || fragmentCode == "" )return;

	const char* vShaderCode = vertexCode.c_str();
	const char * fShaderCode = fragmentCode.c_str();

	GLuint vertex, fragment, geometry;

	// vertex shader
	vertex = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertex, 1, &vShaderCode, NULL);
	glCompileShader(vertex);
	if (!CheckError(vertex, VERTEX_SHADER))return;

	// fragment Shader
	fragment = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragment, 1, &fShaderCode, NULL);
	glCompileShader(fragment);
	if (!CheckError(vertex, FRAGMENT_SHADER))return;

	// geometry Shader
	if (geometryPath != nullptr) {
		if (geometryCode == "") return;

		const char * gShaderCode = geometryCode.c_str();
		geometry = glCreateShader(GL_GEOMETRY_SHADER);
		glShaderSource(geometry, 1, &gShaderCode, NULL);
		glCompileShader(geometry);
		if (!CheckError(geometry, GEOMETRY_SHADER))return;
	}

	// shader Program
	ID = glCreateProgram();
	glAttachShader(ID, vertex);
	glAttachShader(ID, fragment);
	if (geometryPath != nullptr)
		glAttachShader(ID, geometry);
	glLinkProgram(ID);
	CheckError(ID, SHADER_PROGRAM);

	glDeleteShader(vertex);
	glDeleteShader(fragment);
	if (geometryPath != nullptr)
		glDeleteShader(geometry);
}

std::string Shader::LoadShader(const char* fileName) {
	std::string code = "";
	std::ifstream file;

	file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
	try	{
		file.open(fileName);
		std::stringstream vShaderStream, fShaderStream;
		// read file's buffer contents into streams
		vShaderStream << file.rdbuf();
		// close file handlers
		file.close();
		// convert stream into string
		code = vShaderStream.str();
	}
	catch (std::ifstream::failure e) {
		Util::err("Load file failed ! fielPath : %s", fileName);
	}

	return code;
}

GLvoid Shader::Use() {
	glUseProgram(ID);
}

GLboolean Shader::CheckError(GLuint shader, CheckTYPE type) {
	GLint success;
	GLchar infoLog[1024];
	switch (type)
	{
	case Shader::VERTEX_SHADER:
		glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(shader, 1024, NULL, infoLog);
			Util::err("Vertex shader compile error! \n%s", infoLog);
			return GL_FALSE;
		}
		break;
	case Shader::FRAGMENT_SHADER:
		glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(shader, 1024, NULL, infoLog);
			Util::err("Fragment shader compile error! \n%s", infoLog);
			return GL_FALSE;
		}
		break;
	case Shader::GEOMETRY_SHADER:
		glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
		if (!success) {
			glGetShaderInfoLog(shader, 1024, NULL, infoLog);
			Util::err("Geometry shader compile error! \n%s", infoLog);
			return GL_FALSE;
		}
		break;
	case Shader::SHADER_PROGRAM:
		glGetProgramiv(shader, GL_LINK_STATUS, &success);
		if (!success) {
			glGetProgramInfoLog(shader, 1024, NULL, infoLog);
			Util::err("Shader program link err! \n%s", infoLog);
			return GL_FALSE;
		}
		break;
	default:
		Util::err("Unknow checkType !");
		return GL_FALSE;
		break;
	}

	return GL_TRUE;
}